#!/usr/bin/python

import sys, re, random

def main():
    txtFile = open(sys.argv[1])
    inputText = txtFile.read()
    txtFile.close()
    keyFile = open(sys.argv[2])
    key=keyFile.read().replace('\n','')
    keyFile.close()
    
    
    #step 1 preprocessing
    inputText = re.sub('[^A-Z]+', '', inputText)
    txtSize = len(inputText)
    print 'key ', key
    print 'step1: ', inputText

    #step 2 Substitution
    cipherText = []
    i = 0
    for chars in inputText:
        ctValue = ((ord(chars)-65) + (ord(key[i%len(key)])-65))%26
        cipherText.append(chr(ctValue+65))
        i+=1
    #print 'step2 ciphertext: ', cipherText

    #step 3 padding
    while len(cipherText)%16 != 0:
      cipherText.append('A')
   # print 'cipher text padded: ', cipherText
   
    #step 4 shifting rows, building 4x4 blocks and converting elements to hex
    bloks = []
    z=0
    while z<len(cipherText):
        cube=[]
        y=0
        while y<4:
            row=[]
            x=0
            while x<4:
                row.append(par_bit_flip(ord(cipherText[z])))
                z+=1
                x+=1
          #shift row
            i=0
            while i<y:
                row.append(row.pop(0))
                i+=1
            cube.append(row)
            row = []
            y+=1
        bloks.append(cube)        
    print"block0: ", bloks[0]
    mix_columns(bloks[0])


#step 5: parity bit
#int arg, flips parity bit if odd parity, returns int
def par_bit_flip(val):
    if par_odd(val)==-1:
        val=val^128
    return(val)

#influenced by parityOf "Bit Twiddling Hacks"
#int arg, returns -1 if odd par, 0 if even
def par_odd(i):
    par=0
    while(i):
        par= ~par
        i = i & (i-1)
    return(par)

#step 6: mix columns
def mix_columns(matrix):
    mds=[[2,3,1,1],[1,2,3,1],[1,1,2,3],[3,1,1,2]]
    op_table={1:(lambda x: x), 2:(lambda x: x<<1), 3:(lambda x: x<<1 ^ x)}
    mor=[]
    z=0
    while z<4:
        x=0
        neo=[]
        while x<4:
            y=0
            r=0
            while y<4:
                op = op_table.get(mds[x][y])
                r^=op(matrix[y][x])
                y+=1
            neo.append(r)
            x+=1
        mor.append(neo)
        z+=1
    print"more: ", mor
    
if __name__ == '__main__':
    main()
